﻿using System;
using System.Reflection;
using System.Threading;

namespace FscNET.Apm
{
    public class AsyncResult<TResult> : AsyncResult
    {
        // Fields
        private TResult m_result;
        private static readonly AsyncCallback s_AsyncCallbackHelper;
        private static readonly WaitCallback s_WaitCallbackHelper;

        // Methods
        static AsyncResult()
        {
            s_AsyncCallbackHelper = AsyncCallbackCompleteOpHelperWithReturnValue;
            s_WaitCallbackHelper = WaitCallbackCompleteOpHelperWithReturnValue;
        }

        public AsyncResult(AsyncCallback asyncCallback, object state)
            : base(asyncCallback, state)
        {
        }

        public AsyncResult(AsyncCallback asyncCallback, object state, object initiatingObject)
            : base(asyncCallback, state, initiatingObject)
        {
        }

        private static void AsyncCallbackCompleteOpHelperWithReturnValue(IAsyncResult otherAsyncResult)
        {
            ((AsyncResult<TResult>)otherAsyncResult.AsyncState).CompleteOpHelper(otherAsyncResult);
        }

        protected new IAsyncResult BeginInvokeOnWorkerThread()
        {
            ThreadPool.QueueUserWorkItem(s_WaitCallbackHelper, this);
            return this;
        }

        private void CompleteOpHelper(IAsyncResult ar)
        {
            TResult result = default(TResult);
            Exception exception = null;
            try
            {
                result = OnCompleteOperation(ar);
            }
            catch (Exception exception2)
            {
                exception = (exception2 is TargetInvocationException) ? exception2.InnerException : exception2;
            }
            if (exception == null)
            {
                SetAsCompleted(result, false);
            }
            else
            {
                SetAsCompleted(exception, false);
            }
        }

        public new TResult EndInvoke()
        {
            base.EndInvoke();
            return m_result;
        }

        protected new static AsyncCallback GetAsyncCallbackHelper()
        {
            return s_AsyncCallbackHelper;
        }

        protected new virtual TResult OnCompleteOperation(IAsyncResult result)
        {
            return default(TResult);
        }

        public void SetAsCompleted(TResult result, bool completedSynchronously)
        {
            m_result = result;
            SetAsCompleted(null, completedSynchronously);
        }

        private static void WaitCallbackCompleteOpHelperWithReturnValue(object o)
        {
            ((AsyncResult<TResult>)o).CompleteOpHelper(null);
        }
    }


}
